visited: &mut HashSet<String>) -> CargoResult<()> {
if feat.is_empty() { return Ok(()) }
- // If this feature is of the form `foo/bar`, then we just lookup package
- // `foo` and enable its feature `bar`. Otherwise this feature is of the
- // form `foo` and we need to recurse to enable the feature `foo` for our
- // own package, which may end up enabling more features or just enabling
- // a dependency.
- let mut parts = feat.splitn(2, '/');
- let feat_or_package = parts.next().unwrap();
- match parts.next() {
- Some(feat) => {
- let package = feat_or_package;
- used.insert(package.to_string());
- deps.entry(package.to_string())
- .or_insert(Vec::new())
- .push(feat.to_string());
- }
- None => {
- let feat = feat_or_package;
- if !visited.insert(feat.to_string()) {
- bail!("Cyclic feature dependency: feature `{}` depends \
- on itself", feat)
- }
- used.insert(feat.to_string());
- match s.features().get(feat) {
- Some(recursive) => {
- for f in recursive {
- try!(add_feature(s, f, deps, used, visited));
+ if !visited.insert(feat.to_string()) {
+ bail!("Cyclic feature dependency: feature `{}` depends \
+ on itself", feat)
+ }
+
+ used.insert(feat.to_string());
+
+ // The lookup of here may fail if the feature corresponds to an optional
+ // dependency. If that is the case, we simply enable the optional dependency.
+ //
+ // Otherwise, we check what other features the feature wants and recursively
+ // add them.
+ match s.features().get(feat) {
+ Some(wanted_features) => {
+ for entry in wanted_features {
+ // If the entry is of the form `foo/bar`, then we just lookup package
+ // `foo` and enable its feature `bar`. We also add `foo` to the used
+ // set because `foo` might have been an optional dependency.
+ //
+ // Otherwise the entry refers to another feature of our current package,
+ // so we recurse by calling add_feature again, which may end up enabling
+ // more features or just enabling a dependency (remember, optional
+ // dependencies create an implicit feature with the same name).
+ let mut parts = entry.splitn(2, '/');
+ let feat_or_package = parts.next().unwrap();
+ match parts.next() {
+ Some(feat) => {
+ let package = feat_or_package;
+ used.insert(package.to_string());
+ deps.entry(package.to_string())
+ .or_insert(Vec::new())
+ .push(feat.to_string());
+ }
+ None => {
+ let feat = feat_or_package;
+ try!(add_feature(s, feat, deps, used, visited));
}
- }
- None => {
- deps.entry(feat.to_string()).or_insert(Vec::new());
}
}
- visited.remove(&feat.to_string());
+ }
+ None => {
+ deps.entry(feat.to_string()).or_insert(Vec::new());
}
}
+
+ visited.remove(&feat.to_string());
+
Ok(())
}
}
continue
}
let mut base = feature_deps.remove(dep.name()).unwrap_or(vec![]);
- for feature in dep.features().iter() {
- base.push(feature.clone());
+ base.extend(dep.features().iter().map(|x| x.clone()));
+ for feature in base.iter() {
if feature.contains("/") {
- bail!("features in dependencies cannot enable features in \
- other dependencies: `{}`", feature)
+ bail!("feature names may not contain slashes: `{}`", feature);
}
}
ret.push((dep.clone(), base));
assert_that(p.cargo_process("build").arg("--features").arg("foo"),
execs().with_status(101).with_stderr("\
-[ERROR] features in dependencies cannot enable features in other dependencies: `foo/bar`
+[ERROR] feature names may not contain slashes: `foo/bar`
+"));
+}
+
+#[test]
+fn no_transitive_dep_feature_requirement() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies.derived]
+ path = "derived"
+
+ [features]
+ default = ["derived/bar/qux"]
+ "#)
+ .file("src/main.rs", r#"
+ extern crate derived;
+ fn main() { derived::test(); }
+ "#)
+ .file("derived/Cargo.toml", r#"
+ [package]
+ name = "derived"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies.bar]
+ path = "../bar"
+ "#)
+ .file("derived/src/lib.rs", r#"
+ extern crate bar;
+ pub use bar::test;
+ "#)
+ .file("bar/Cargo.toml", r#"
+ [package]
+ name = "bar"
+ version = "0.0.1"
+ authors = []
+
+ [features]
+ qux = []
+ "#)
+ .file("bar/src/lib.rs", r#"
+ #[cfg(feature = "qux")]
+ pub fn test() { print!("test"); }
+ "#);
+ assert_that(p.cargo_process("build"),
+ execs().with_status(101).with_stderr("\
+[ERROR] feature names may not contain slashes: `bar/qux`
"));
}